#pragma once 
#include "exception.h"

namespace AppliedCryptography
{
	namespace AC = AppliedCryptography;

	template<unsigned int TBlockSize>
	class Block
	{
	private:
		std::vector<byte> _content;
		unsigned int _size;

	public:
		Block() : _size(TBlockSize)
		{
			_content.resize(TBlockSize,0);  // Size() >> 3 == Size()/8 == count of bytes (not bits)
		};

		explicit Block(const std::vector<byte>& contentBytes) : _size(TBlockSize), _content(contentBytes)
		{
		};

		virtual ~Block()
		{ /*...*/ };

	public:
		virtual unsigned int Size() const
		{
			return _size;
		};

		virtual const byte& operator[] (unsigned int index) const
		{
			if(index >= _size)
				throw AC::Exception("Index too large!");

			return _content.at(index);
		};

		virtual byte& operator[] (unsigned int index)
		{
			if(index >= _size)
				throw AC::Exception("Index too large!");

			return _content.at(index);
		};

		virtual Block<TBlockSize>& Set(const byte* contentByteArray)
		{
			unsigned int len = this->Size();

			for(unsigned int i=0; i < len; i++)
			{
				_content.at(i) = contentByteArray[i];
			};

			return *this;
		};

		virtual Block<TBlockSize>& Set(const std::vector<byte>& contentBytes)
		{
			std::vector<byte>::iterator it;
			int index = 0;

			if(_content.size() != contentBytes.size())
				throw AC::Exception("Content sizes differ!");

			for(it = _content.begin(); it != _content.end(); it++)
			{
				*it = contentBytes[index];
				++index;
			};

			return *this;
		};

		virtual std::vector<byte> Get() const
		{
			std::vector<byte> contentBytes;
			std::vector<byte>::const_iterator it;
			int index = 0;

			contentBytes.resize(_content.size(), 0);

			for(it = _content.begin(); it != _content.end(); it++)
			{
				contentBytes.at(index) = *it;
				++index;
			};

			return contentBytes;
		};

		virtual bool operator== (const Block<TBlockSize>& other) const
		{
			return _content == other._content;
		};

		virtual bool operator!= (const Block<TBlockSize>& other) const
		{
			return !(*this == other);
		};

		virtual const Block<TBlockSize> operator^(const Block<TBlockSize>& other) const
		{
			Block<TBlockSize> result = *this;
			result ^= other;
			return result;
		};

		virtual Block<TBlockSize>& operator^=(const Block<TBlockSize>& other)
		{
			for(unsigned int i=0; i < TBlockSize; i++)
				_content[i] ^= other[i];

			return *this;
		};
	};
};
